package com.heaboy.shiwu;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class TransactionProxy {
    private final TransactionManager transactionManager;

    public TransactionProxy(TransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }

    @SuppressWarnings("unchecked")
    public <T> T createProxy(T target) {
        // 代理策略选择：有接口实现用JDK动态代理，无接口实现用CGLIB
        if (target.getClass().getInterfaces().length > 0) {
            return createJdkProxy(target);
        } else {
            return createCglibProxy(target);
        }
    }

    // JDK动态代理
    private <T> T createJdkProxy(T target) {
        return (T) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new TransactionInvocationHandler(target, transactionManager)
        );
    }

    // CGLIB动态代理
    private <T> T createCglibProxy(T target) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(new CglibTransactionInterceptor(target, transactionManager));
        return (T) enhancer.create();
    }

    // JDK代理处理器
    private static class TransactionInvocationHandler implements InvocationHandler {
        private final Object target;
        private final TransactionManager transactionManager;

        public TransactionInvocationHandler(Object target, TransactionManager transactionManager) {
            this.target = target;
            this.transactionManager = transactionManager;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (method.isAnnotationPresent(Transactional.class)) {
                return handleTransactionalMethod(method, args);
            } else {
                return method.invoke(target, args);
            }
        }

        private Object handleTransactionalMethod(Method method, Object[] args) throws Throwable {
            Object result = null;
            try {
                transactionManager.beginTransaction();
                System.out.println("开启事务(JDK代理)...");

                result = method.invoke(target, args);

                transactionManager.commit();
                System.out.println("提交事务(JDK代理)...");
            } catch (Exception e) {
                transactionManager.rollback();
                System.out.println("回滚事务(JDK代理)，原因: " + (e.getCause() != null ? e.getCause().getMessage() : e.getMessage()));
                throw e.getCause() != null ? e.getCause() : e;
            }
            return result;
        }
    }

    // CGLIB代理处理器
    private static class CglibTransactionInterceptor implements MethodInterceptor {
        private final Object target;
        private final TransactionManager transactionManager;

        public CglibTransactionInterceptor(Object target, TransactionManager transactionManager) {
            this.target = target;
            this.transactionManager = transactionManager;
        }

        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            if (method.isAnnotationPresent(Transactional.class)) {
                return handleTransactionalMethod(method, args);
            } else {
                return method.invoke(target, args);
            }
        }

        private Object handleTransactionalMethod(Method method, Object[] args) throws Throwable {
            Object result = null;
            try {
                transactionManager.beginTransaction();
                System.out.println("开启事务(CGLIB代理)...");

                result = method.invoke(target, args);

                transactionManager.commit();
                System.out.println("提交事务(CGLIB代理)...");
            } catch (Exception e) {
                transactionManager.rollback();
                System.out.println("回滚事务(CGLIB代理)，原因: " + (e.getCause() != null ? e.getCause().getMessage() : e.getMessage()));
                throw e.getCause() != null ? e.getCause() : e;
            }
            return result;
        }
    }
}